package main

import (
	"flag"
	"gitee.com/yysf_xin/dplog/pkg/agg"
	"gitee.com/yysf_xin/dplog/pkg/ck"
	"gitee.com/yysf_xin/dplog/pkg/config"
	"gitee.com/yysf_xin/dplog/pkg/push"
	"gitee.com/yysf_xin/dplog/pkg/slog"
	"gitee.com/yysf_xin/dplog/pkg/web"
	"gitee.com/yysf_xin/go-common/db"
	"gitee.com/yysf_xin/go-common/tools"
	"gitee.com/yysf_xin/go-notifier"
	_ "gitee.com/yysf_xin/go-notifier/http"
	"github.com/go-chi/chi/v5"
	"gopkg.in/yaml.v3"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
)

//func serveHome(w http.ResponseWriter, r *http.Request) {
//	slog.Println(r.URL)
//	if r.URL.Path != "/" {
//		http.Error(w, "Not found", http.StatusNotFound)
//		return
//	}
//	if r.Method != http.MethodGet {
//		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
//		return
//	}
//
//}

type Option struct {
	Addr       string
	Config     string
	ServeModel int
	Debug,
	Upgrade bool
	Remote string
}

var option Option

func main() {
	//log.Default().SetFlags(log.Lshortfile | log.LstdFlags)
	flag.StringVar(&option.Addr, "l", ":8080", "http server addr")
	flag.StringVar(&option.Config, "c", "slog.yml", "slog config file")
	flag.IntVar(&option.ServeModel, "s", 0, "1 enable push server")
	flag.BoolVar(&option.Upgrade, "u", false, "upgrade db")
	flag.StringVar(&option.Remote, "r", "", "remote config server")
	flag.StringVar(&config.Ip, "ip", "", "local ip addr")
	flag.StringVar(&config.EnvStr, "e", "", "dev,sit,prod")

	//flag.Float64Var(&option.Threshold, "v", 0.6, "similarity threshold")
	//flag.BoolVar(&option.Debug,"debug",false,"enable pprof")
	flag.Parse()
	if config.Ip == "" {
		config.Ip, _ = getOutBoundIP()
	}
	cfg, err := config.ParseConfig(option.Config)

	if err != nil {
		log.Println(err)
	}

	if option.Remote != "" {
		var (
			host string
			resp *http.Response
			conf config.ServerConfig
		)
		host, _ = os.Hostname()
		if strings.Contains(option.Remote, "?") {
			option.Remote += "&"
		} else {
			option.Remote += "?"
		}
		option.Remote += "host=" + host + "&ip=" + config.Ip
		tools.Log.Println("get remote config", option.Remote)
		resp, err = http.DefaultClient.Get(option.Remote + "")
		if err != nil {
			log.Fatalln(err)
			return
		}
		if resp.StatusCode == 200 {
			err = yaml.NewDecoder(resp.Body).Decode(&conf)
			if err != nil {
				log.Fatalln(err)
				return
			}

			if conf.ClickHouse == nil {
				conf.ClickHouse = cfg.ClickHouse
			}

			if conf.Dirs == nil || len(conf.Dirs) == 0 {
				conf.Dirs = cfg.Dirs
			}

			cfg = &conf

		}
		if resp.Body != nil {
			err = resp.Body.Close()
		}
	}

	if cfg == nil {
		log.Println("load config error", err)
		return
	}

	if len(cfg.Dirs) == 0 && option.ServeModel != 1 {
		log.Println("dir config is empty", err)
		return
	}

	var (
		dbClient *db.Client
		ntClient *notifier.Notifier
	)

	if cfg.Db != nil {
		cfg.Db.Upgrade = option.Upgrade
		dbClient, err = db.NewClient(cfg.Db, nil)
		if err != nil {
			log.Fatalln(err)
			return
		}
		ntClient, err = notifier.New(dbClient)
		if err != nil {
			log.Fatalln(err)
			return
		}
	}

	var nt push.MsgNotifier
	if option.ServeModel == 1 {
		if dbClient == nil {
			log.Fatalln("db config not exists")
			return
		}

		var st *push.Server
		st, err = push.NewServer(dbClient, ntClient)
		if err != nil {
			log.Fatalln(err)
			return
		}
		if cfg.Push != nil {
			st.Template = cfg.Push.Template
		}
		if st.Template == "" {
			st.Template = "LOG_NOTIFY"
		}
		go st.Run()
		nt = st
	} else {
		if cfg.Push != nil {
			if cfg.Push.Url != "" {
				tools.Log.Println("start push url", cfg.Push.Url)
				ht := push.NewHttp(cfg.Push.Url)
				nt = ht
				go ht.Run()
			}
		} else {
			tools.Log.Println("push url is null")
		}
	}
	ems := slog.NewEmitters()
	var logAgg *agg.LogAggregation
	if cfg.Agg != nil && nt != nil {
		logAgg = agg.New(cfg.Agg, nt)
		ems.Append(logAgg)
	}

	if cfg.ClickHouse != nil {
		var ckc *ck.Collect
		ckc, err = ck.NewCollect(cfg.ClickHouse)
		tools.Log.Println("init clickhouse", cfg.ClickHouse.Table)
		if err != nil {
			return
		}
		ems.Append(ckc)
	}

	s, err := web.NewLogServer(cfg, ems)
	if err != nil {
		if s != nil {
			_ = s.Close()
		}
		return
	}

	r := chi.NewRouter()
	r.HandleFunc("/ws", s.ServeWs)
	r.Route("/api/", func(c chi.Router) {
		if ntClient != nil {
			c.Post("/push", ntClient.ServeHTTP)
		}
		c.HandleFunc("/watch/*", s.ServeWatch)
		c.Route("/dir/", s.ServeDir)
		if option.ServeModel == 1 {
			sv := nt.(*push.Server)
			sv.Em = logAgg
			ch := config.NewHandler(dbClient)
			c.Route("/log/", func(r chi.Router) {
				r.Post("/push", sv.ServeHTTP)
				r.Get("/list", sv.ListLog)
				r.Get("/config", ch.ServeHTTP)
			})
		}
	})

	go s.Run()
	tools.Log.Println("start at", option.Addr)
	err = http.ListenAndServe(option.Addr, r)

	if err != nil {
		log.Fatalln(err)
	}
	log.Println("init")

	err = s.Close()
}

func getOutBoundIP() (ip string, err error) {
	conn, err := net.Dial("udp", "114.114.114.114:53")
	if conn != nil {
		defer conn.Close()
	}
	if err != nil {
		tools.Log.Println(err)
		return
	}
	localAddr := conn.LocalAddr().(*net.UDPAddr)
	ip = strings.Split(localAddr.String(), ":")[0]
	return
}
